/*
 * Copyright (c) 2010, VeRSI Consortium
 *   (Victorian eResearch Strategic Initiative, Australia)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the VeRSI, the VeRSI Consortium members, nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package versi.vitro.oaiserviceprovider.service.jena.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;

import versi.vitro.oaiserviceprovider.service.jena.api.SparqlQueryEngine;

import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.Syntax;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;

// Wrapper around static methods so it can be integrated with spring and the testing code.
// Also wraps the conversion of checked exceptions into unchecked exceptions.
/**
 * Service implementing {@link SparqlQueryEngine}.
 */
public class SparqlQueryEngineImpl implements SparqlQueryEngine {

    /** log4j instance. */
    private static final Logger LOGGER = Logger.getLogger(SparqlQueryEngineImpl.class);

    protected static Set<String> validRdfResultFormats = new HashSet<String>();
    static {
	validRdfResultFormats.addAll(Arrays.asList("RDF/XML", "RDF/XML-ABBREV", "N3", "N-Triples", "Turtle"));
    }

    protected static Set<String> validResultFormats = new HashSet<String>();
    static {
	validResultFormats.addAll(Arrays.asList("RS_XML", "RS_TEXT", "CSV", "RS_RDF/N3", "RS_JSON", "RS_RDF"));
    }

    protected static final Syntax SYNTAX = Syntax.syntaxSPARQL; // .syntaxARQ;

    /** {@inheritDoc} */
    public void populate(final String sparql, final OntModel sourceOntologyModel, final Model destinationModel) {
	Query query = QueryFactory.create(sparql, Syntax.syntaxARQ);
	QueryExecution qexec = QueryExecutionFactory.create(query, sourceOntologyModel);
	try {
	    qexec.execConstruct(destinationModel);
	} catch (Exception exception) {
	    String message = "Unable to populate the model using SPARQL. ";
	    LOGGER.fatal(message, exception);
	    throw new RuntimeException(message, exception);
	} finally {
	    qexec.close();
	}
    }

    /** {@inheritDoc} */
    public byte[] executeSparqlReturnCSV(final String sparql, final Model sourceModel) {

	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	// ResultSet results = null;

	// if ((!validateRdfResultFormat(rdfResultFormat)) ||
	// (!validateResultFormat(resultFormat))) {

	Query query = QueryFactory.create(sparql);
	QueryExecution queryExecution = QueryExecutionFactory.create(query, sourceModel);

	// test the query is select statement
	// query.isSelectType();

	try {
	    ResultSet results = (ResultSet) queryExecution.execSelect();

	    while (results.hasNext()) {

		QuerySolution solution = (QuerySolution) results.next();
		// final Literal name = solution.getLiteral("x");

		RDFNode n = solution.get("x");
		if (n.isLiteral())
		    ((Literal) n).getLexicalForm();
		if (n.isResource()) {
		    Resource r = (Resource) n;
		    if (!r.isAnon()) {

			LOGGER.debug(r.getURI());
			if (baos.size() == 0) {
			    baos.write(r.getURI().getBytes());
			} else {
			    String temp = "," + r.getURI();
			    baos.write(temp.getBytes());
			}
			// ... r.getURI() ...
		    } else {
			LOGGER.debug(r.getId());

		    }
		}

	    }

	} catch (IOException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} finally {
	    queryExecution.close();
	}

	return baos.toByteArray();

    }

    /** {@inheritDoc} */
    public boolean validateRdfResultFormat(final String rdfResultFormat) {

	return validRdfResultFormats.contains(rdfResultFormat);
    }

    /** {@inheritDoc} */
    public boolean validateResultFormat(final String resultFormat) {

	return validResultFormats.contains(resultFormat);
    }

}
